using System;
using System.Collections.Generic;
using System.Linq;
using Shutterstock.Salesforce.Tools.SFPartner;
using System.Xml;
using Shutterstock;
using System.Globalization;
using System.Data;

namespace Shutterstock.Salesforce.Tools
{
    public static class ExtensionMethods
    {
        public static sObject ToSObject(this ISalesforceEntity entity)
        {
            var att = entity.GetType().GetCustomAttributes(typeof(SalesforceEntitiyAttribute), false).OfType<SalesforceEntitiyAttribute>().FirstOrDefault();

            if (att == null)
            {
                throw new InvalidOperationException("the specified type does not have a SalesforceEntityAttribute");
            }

            sObject result = new sObject();
            result.type = att.SObjectType;
            result.Any = BuildLineItems(entity).ToArray();
            result.Id = GetIdentity(entity);
            return result;
        }

        private static string GetIdentity(ISalesforceEntity entity)
        {
            var qry =
                from p in entity.GetType().GetProperties()
                from att in p.GetCustomAttributes(typeof(SalesforceColumnAttribute), false).OfType<SalesforceColumnAttribute>()
                where att.IsIdentity
                select new { property = p, attribute = att };

            var prop = qry.FirstOrDefault();

            if (prop == null)
            {
                return null;
            }

            object value = prop.property.GetValue(entity, null);
            var result = (value == null)? String.Empty: value.ToString();
            return result;
        }

        private static IEnumerable<XmlElement> BuildLineItems(ISalesforceEntity entity)
        {
            XmlDocument doc = new XmlDocument();

            var cols =
                from x in
                    (
                        from prop in entity.GetType().GetProperties()
                        select new
                        {
                            prop = prop,
                            att = prop.GetCustomAttributes(typeof(SalesforceColumnAttribute), false).OfType<SalesforceColumnAttribute>().FirstOrDefault()
                        })
                where x.att != null
                select x;

            foreach (var col in cols)
            {
                System.Reflection.PropertyInfo property = col.prop;

                var value = GetStringValue(entity, property);

                bool valueIsEmpty = String.IsNullOrEmpty(value);

                // SET DEFAULT VALUE
                if (valueIsEmpty && !String.IsNullOrEmpty(col.att.DefaultValue))
                {
                    value = col.att.DefaultValue;
                    valueIsEmpty = false;
                }

                // IGNORE NULLS
                if (valueIsEmpty && !col.att.IncludeIfNullOrWhitespace)
                {
                    continue;
                }

                // TRIM TO MAX LENGTH
                if (col.att.MaxWidth > 0 && !valueIsEmpty && value.Length > col.att.MaxWidth)
                {
                    value = value.Substring(0, Math.Min(value.Length, 40));
                }

                var key = col.att.Name ?? col.prop.Name;
                var result = doc.CreateElement(key);
                result.InnerText = value ?? String.Empty;
                yield return result;
            }
        }

        private static string GetStringValue(ISalesforceEntity entity, System.Reflection.PropertyInfo property)
        {
            object state = property.GetValue(entity, null);

            if (property.PropertyType == typeof(DateTime))
            {
                var dt = (DateTime)state;
                return dt.ToYYYYMMDD();
            }

            if (property.PropertyType == typeof(DateTime?))
            {
                var ndt = (DateTime?)state;
                return ndt.ToYYYYMMDD();
            }

            var value = (state ?? String.Empty).ToString();
            return value;
        }


        public static String ToYYYYMMDD(this DateTime date)
        {
            var result = date.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
            return result;
        }

        public static String ToYYYYMMDD(this DateTime? date)
        {
            return date.HasValue ? date.Value.ToYYYYMMDD() : String.Empty;
        }
    }
}
